package gu.jimgutil;

/**
 * 色彩空间转换通用实现<br>
 * 当没有支持当前平台的native library时,调用此类对应的方法,会比native函数慢很多
 * @author guyadong
 *
 */
class CsCvtUniImpl {

	/**
	 * 将输入图像的色彩空间RGBA转为BGR，输出到output
	 * @param input 输入图像矩阵RGBA,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*4
	 * @param output [out] 输出图像,size=width*height*3
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void RGBA2BGR(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*4 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height * 3) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int y = 0; y < height; ++y) {
			for (int x = 0,p=0; x < width*4; x +=4, p+=3) {
				output[output_line+ p       ] = input[input_line + x + 2] ; // BLUE
				output[output_line+ p + 1 ] = input[input_line + x + 1] ; // GREEN
				output[output_line+ p + 2 ] = input[input_line + x      ] ; // RED
			}
			input_line  += stride;
			output_line += width * 3;
		}
	}
	/**
	 * 将输入图像的色彩空间RGBA转为RGB，输出到output
	 * @param input 输入图像矩阵RGBA,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*4
	 * @param output [out] 输出图像,size=width*height*3
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void RGBA2RGB(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*4 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height * 3) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int i = 0; i < height; ++i) {
			for (int x = 0,p=0; x < width*4; x +=4, p+=3) {
				output[output_line+ p       ] = input[input_line + x      ] ; // BLUE
				output[output_line+ p + 1 ] = input[input_line + x + 1] ; // GREEN
				output[output_line+ p + 2 ] = input[input_line + x + 2] ; // RED
			}
			input_line  += stride;
			output_line += width * 3;
		}
	}
	/**
	 * 将输入图像的色彩空间RGBA转为灰度，输出到output
	 * @param input 输入图像矩阵RGBA,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*4
	 * @param output [out] 输出图像,size=width*height
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void RGBA2GRAY(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*4 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height ) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int y = 0; y < height; ++y) {
			for (int x = 0,p=0; x < width; ++x, p += 4) {
				int B = input[input_line + p       ] ; // BLUE
				int G = input[input_line + p + 1 ] ; // GREEN
				int R = input[input_line + p + 2 ] ; // RED
				output[output_line + x] = (byte)((R * 76 + G * 150 + B * 30) >> 8);
			}
			input_line  += stride;
			output_line += width;
		}
	}
	/**
	 * 将输入图像的色彩空间RGB转为BGR，输出到output
	 * @param input 输入图像矩阵RGBA,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*3
	 * @param output [out] 输出图像,size=width*height*3
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void RGB2BGR(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*3 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height * 3 ) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int y = 0; y < height; ++y) {
			for (int x = 0; x < width * 3; x += 3) {
				output[output_line+ x       ] = input[input_line + x + 2 ] ; // BLUE
				output[output_line+ x + 1 ] = input[input_line + x + 1 ] ; // GREEN
				output[output_line+ x + 2 ] = input[input_line + x       ] ; // RED
			}
			input_line += stride;
			output_line += width * 3;
		}
	}
	/**
	 * 将输入图像的3字节色彩空间(RGB/BGR)反序转换
	 * @param input 输入图像矩阵(RGB/BGR),size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*3
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void swap3byte(byte[] input,int width,int height,int stride) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input){
			throw new NullPointerException();
		}
		if (stride < width*3 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}

		int input_line=0;
		byte swap;
		for (int y = 0; y < height; ++y) {
			for (int x = 0; x < width * 3; x += 3) {
				swap = input[input_line + x + 2 ] ; 
				input[input_line + x + 2 ]  = input[input_line + x       ] ; 
				input[input_line + x       ] = swap;
			}
			input_line += stride;
		}
	}
	/**
	 * 将输入图像的色彩空间RGB转为灰度，输出到output
	 * @param input 输入图像矩阵RGB,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*3
	 * @param output [out] 输出图像,size=width*height
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void RGB2GRAY(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*3 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height ) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int y = 0; y < height; ++y) {
			for (int x = 0,p=0; x < width; ++x, p += 3) {
				int B = input[input_line + p       ] ; // BLUE
				int G = input[input_line + p + 1 ] ; // GREEN
				int R = input[input_line + p + 2 ] ; // RED
				output[output_line + x] = (byte)((R * 76 + G * 150 + B * 30) >> 8);
			}
			input_line += stride;
			output_line += width;
		}
	}
	/**
	 * 将输入图像的色彩空间BGR转为RGBA，输出到output
	 * @param input 输入图像矩阵BGR,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*3
	 * @param output [out] 输出图像,size=width*height*4
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void BGR2RGBA(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*3 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height * 4) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int y = 0; y < height; ++y) {
			for (int x = 0,p=0; x < width*3; x +=3, p+=4) {
				output[output_line+ p       ] = input[input_line + x + 2] ; // RED
				output[output_line+ p + 1 ] = input[input_line + x + 1] ; // GREEN
				output[output_line+ p + 2 ] = input[input_line + x      ] ; // BLUE
				output[output_line+ p + 3 ] = 0 ; 
			}
			input_line  += stride;
			output_line += width * 4;
		}
	}
	/**
	 * 将输入图像的色彩空间RGB转为RGBA，输出到output
	 * @param input 输入图像矩阵RGB,size=stride*height
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param stride 每行步长(字节长度),必须>= width*3
	 * @param output [out] 输出图像,size=width*height*4
	 * @throws NullPointerException 
	 * @throws IllegalArgumentException
	 */
	public static void RGB2RGBA(byte[] input,int width,int height,int stride,byte[] output) 
			throws NullPointerException, IllegalArgumentException{
		if(null == input || null == output){
			throw new NullPointerException();
		}
		if (stride < width*3 || input.length != stride * height) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if (output.length != width * height * 4) {
			throw new IllegalArgumentException("INVALID OUTPUT SIZE");
		}
		int input_line=0,output_line=0;
		for (int i = 0; i < height; ++i) {
			for (int x = 0,p=0; x < width*3; x +=3, p+=4) {
				output[output_line+ p       ] = input[input_line + x      ] ; // RED
				output[output_line+ p + 1 ] = input[input_line + x + 1] ; // GREEN
				output[output_line+ p + 2 ] = input[input_line + x + 2] ; // BLUE
				output[output_line+ p + 3 ] = 0;
			}
			input_line  += stride;
			output_line += width * 4;
		}
	}
	/**
	 * 将输入图像 顺时针原地旋转(In-place matrix transposition)指定的角度<br>
	 * 图像必须是1字节对齐，多字节对齐的图像旋转可能会造成图像错位
	 * @param input 输入图像矩阵,size=width*height*bpp
	 * @param width 图像宽度
	 * @param height 图像宽度
	 * @param bpp 每像素字节数(1,2,3,4)
	 * @param angle 顺时针旋转角度(0,90,180,270)
	 * @throws NullPointerException
	 * @throws IllegalArgumentException
	 */
	public static void rotate(byte[] input,int width,int height,int bpp,int angle) {
		if(input == null){
			throw new NullPointerException("input is null");
		}
		if (input.length != width * height * bpp) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if(bpp<1 || bpp >4 ){
			throw new IllegalArgumentException("INVALID bpp,available value:1,2,3,4");
		}
		switch(angle){
		case 0:
			return;
		case 90:
			rotate90(input, width, height, bpp);
			return;
		case 180:
			rotate180(input, width, height, bpp);
			return;
		case 270:
			rotate270(input, width, height, bpp);
			return;
		default:
			throw new IllegalArgumentException("INVALID angle,available value:0,90,180,270");				
		}
	}
	/**
	 * 对数据array中x和y指向的数据交换
	 * @param array 
	 * @param x 
	 * @param y
	 * @param size 数据交换长度
	 * @param tmp 用于数据交换的临时缓冲区，长度必须>= size
	 */
	private static void swap(byte[] array,int x,int y, int size, byte[] tmp){
		System.arraycopy(array, x, tmp, 0, size);
		System.arraycopy(array, y, array, x, size);
		System.arraycopy(tmp, 0, array, y, size);
	}
	/**
	 * 图像顺时针旋转90度
	 * @param input
	 * @param width
	 * @param height
	 * @param bpp
	 */
	private static void rotate90(byte[] input,int width,int height,int bpp) {
		byte[] tmp = new byte[bpp];
		for (int i = 0; i < width; i++) 
		{
			for (int j = 0; j < height; j++) 
			{
				int I = height - 1 - j;
				int J = i;
				while ((i*height + j) > (I*width + J))
				{
					int p = I*width + J;
					int tmp_i = p / height;
					int tmp_j = p % height;
					I = height - 1 - tmp_j;
					J = tmp_i;
				} 
				swap(input,(i*height + j)*bpp, (I*width + J)*bpp, bpp, tmp);
			}
		}
	}
	/**
	 * 图像顺时针旋转180度
	 * @param input
	 * @param width
	 * @param height
	 * @param bpp
	 */
	private static void rotate180(byte[] input,int width,int height,int bpp) {
		byte[] s = new byte[bpp];
		int stride = width*bpp;
		int srcline = 0;
		int dstline = (height -1)*stride;
		for(int y=0;  y < ((height+1)>>1); ++y){
			int dx = stride - bpp;
			for(int sx=0;  sx < stride; sx += bpp, dx -= bpp){
				swap(input, srcline+sx, dstline+dx, bpp, s);
			}
			srcline += stride;
			dstline -= stride;
		}
	}
	/**
	 * 图像顺时针旋转270度
	 * @param input
	 * @param width
	 * @param height
	 * @param bpp
	 */
	private static void rotate270(byte[] input,int width,int height,int bpp) {
		byte[] tmp = new byte[bpp];
		for (int i = 0; i < width; i++) 
		{
			for (int j = 0; j < height; j++) 
			{
				int I = j;
				int J = width - 1 - i;
				while ((i*height + j) > (I*width + J))
				{
					int p = I*width + J;
					int tmp_i = p / height;
					int tmp_j = p % height;
					I = tmp_j;
					J = width - 1 - tmp_i;
				} 
				swap(input,(i*height + j)*bpp, (I*width + J)*bpp, bpp, tmp);
			}
		}
		
	}
	/**
	 * 图像水平原地翻转(In-place)<br>
	 * 图像必须是1字节对齐，多字节对齐的图像旋转可能会造成图像错位
	 * @param input 输入图像矩阵,size=width*height*bpp
	 * @param width
	 * @param height
	 * @param bpp 每像素字节数(1,2,3,4)
	 * @throws NullPointerException
	 * @throws IllegalArgumentException
	 */
	public static void hflip(byte[] input,int width,int height,int bpp) {
		if(input == null){
			throw new NullPointerException("input is null");
		}
		if (input.length != width * height * bpp) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if(bpp<1 || bpp >4 ){
			throw new IllegalArgumentException("INVALID bpp,available value:1,2,3,4");
		}
		byte[] tmp = new byte[bpp];
		int line = 0;
		int stride = width*bpp;
		for (int i = 0; i < height; i++) 
		{
			for (int j = 0,end_j=(width>>1)*bpp; j < end_j; j +=bpp) 
			{
				swap(input,line + j, line + (stride-bpp) - j, bpp, tmp);
			}
			line += stride;
		}
	}
	/**
	 * 图像垂直原地翻转(In-place)<br>
	 * 图像必须是1字节对齐，多字节对齐的图像旋转可能会造成图像错位
	 * @param input 输入图像矩阵,size=width*height*bpp
	 * @param width
	 * @param height
	 * @param bpp 每像素字节数(1,2,3,4)
	 * @throws NullPointerException
	 * @throws IllegalArgumentException
	 */
	public static void vflip(byte[] input,int width,int height,int bpp) {
		if(input == null){
			throw new NullPointerException("input is null");
		}
		if (input.length != width * height * bpp) {
			throw new IllegalArgumentException("INVALID INPUT SIZE");
		}
		if(bpp<1 || bpp >4 ){
			throw new IllegalArgumentException("INVALID bpp,available value:1,2,3,4");
		}
		int stride = width*bpp;
		byte[] tmp = new byte[stride];	
		for (int srcline = 0,dstline = (height -1)*stride,end_i=(height>>1)*stride; 
				srcline < end_i; 
				srcline+=stride,dstline -= stride) 
		{			
			swap(input,srcline, dstline, stride, tmp);
		}
	}
}
